package timeline;


import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CountDownLatch;

/**
 *
 * the class for control thread switch
 * by lianghailun
 * Created by lianghailun on 2017/6/20 0020.
 */
public class TimeLine {
    private List<RunningTask> runningTasks;
    private CountDownLatch beforeLatch; // make sure all threads run with main at the same time
    private CountDownLatch afterLatch;  // this latch is to divide level
    private HashMap<String,Object> map;  // the object to deliver msg in all functions
    
    public static class Builder{
    	private List<Object> objects;
    	public Builder() {
			// TODO Auto-generated constructor stub
    		objects = new ArrayList<Object>();
		}
    	
    	public Builder addLib(Object object) {
			objects.add(object);
			return this;
		}
    	
    	public void run(){
    		TimeLine timeLine = new TimeLine();
    		timeLine.startSteinsGate(objects);
    	}
    }
    
    private TimeLine(){
        map = new HashMap<>();
        runningTasks = new ArrayList<>();
    }

    /**
     * run the objects with methods
     */
    private  void  startSteinsGate(List<Object> objs){
        runningTasks.clear();//clear all task
        for (Object obj : objs) {
            trackTask(obj, obj.getClass());//parse each obj thread
        }
        startRun();//start run
    }

    /**
     * run the object with methods
     * @param obj
     */
    private  void  startSteinsGate(final Object obj){
        runningTasks.clear();//clear all task
        trackTask(obj,obj.getClass());//parse one obj thread
        startRun();//start run
    }

    /**
     * start run
     */
    private void startRun() {
        new Thread(){
            @Override
            public void run() {//schdlue all in one thread
                super.run();
                try {

                    for (final RunningTask task : runningTasks) {
                        beforeLatch = new CountDownLatch(1);//set the lautch make sure all run in same time
                        int sum = 0;
                        for(Integer i:task.copys){
                        	sum += i;
                        }
                        afterLatch = new CountDownLatch(sum);// count down all thread in a level
                        for(int i=0;i<task.methods.size();i++){
                        	for(int j=0;j<task.copys.get(i);j++){
                        		new MyThread(task.obj, task.methods.get(i), j).start();;
                        	}
                        }
                        beforeLatch.countDown();
                        try {
                            afterLatch.await();// wait for a level to finish
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        }.start();
    }

    private class MyThread extends Thread{
        private Object obj;
        private Method m;
        private int copy;

        public MyThread(Object obj, Method m,int copy) {
            this.obj = obj;
            this.m = m;
            this.copy = copy;
        }

        @Override
        public void run() {
            super.run();
            try {
                beforeLatch.await();// wait for mainthread
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            try {
            	RunningMsg msg = new RunningMsg();
            	msg.copy = copy;
            	msg.maps = map;
                m.invoke(obj,msg);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
            afterLatch.countDown();// countdown for a level
        }
    }

    /**
     * bind method in a object
     * @param obj
     * @param cl
     */
    public  void trackTask( Object obj,Class<?> cl) {
        for (Method m : cl.getDeclaredMethods()) {
            TLine tick = m.getAnnotation(TLine.class);
            if (tick != null) {
                System.out.println("parsing Level:" + tick.level() + "copy"
                        + tick.copy());
                boolean isArealy = false;
                for(RunningTask runningTask : runningTasks){
                    if(runningTask.level == tick.level()){
                        isArealy = true;
                        runningTask.methods.add(m);
                        runningTask.copys.add(tick.copy());
                        runningTask.obj = obj;
                    }
                }
                if(isArealy){
                    continue;
                }else{
                    RunningTask runningTask = new RunningTask();
                    runningTask.level = tick.level();
                    runningTask.copys = new ArrayList<Integer>();
                    runningTask.methods = new ArrayList<Method>();
                    runningTask.methods.add(m);
                    runningTask.copys.add(tick.copy());
                    runningTask.obj = obj;
                    runningTasks.add(runningTask);
                }
            }
        }
        Collections.sort(runningTasks,new LevelComparator());
    }

    /**
     * sort for level
     */
    private class  LevelComparator implements Comparator{


        @Override
        public int compare(Object o1, Object o2) {
            RunningTask t1= (RunningTask) o1;
            RunningTask t2= (RunningTask) o2;
            return t1.level-t2.level;
        }
    }

    /**
     * model for running
     */
    private class RunningTask{
        public int level = 0;
        public Object obj;
        public List<Method> methods;
        public List<Integer> copys;
    }
    
    /**
     * message for running
     * @author Administrator
     *
     */
    public class RunningMsg{
    	public int copy;
    	public HashMap<String, Object> maps;
    }

    /**
     * Created by Administrator on 2017/6/17 0017.
     */
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface TLine {
        public int level() default 1;
        public int copy() default 1;
    }
}

